package com.wn.video

import android.view.View
import android.widget.FrameLayout
import androidx.core.view.isVisible
import androidx.recyclerview.widget.LinearLayoutManager
import com.alibaba.android.arouter.facade.annotation.Autowired
import com.alibaba.android.arouter.facade.annotation.Route
import com.alibaba.android.arouter.launcher.ARouter
import com.blankj.utilcode.util.ConvertUtils
import com.blankj.utilcode.util.ToastUtils
import com.google.android.exoplayer2.*
import com.google.android.exoplayer2.database.StandaloneDatabaseProvider
import com.google.android.exoplayer2.source.MediaSource
import com.google.android.exoplayer2.source.ProgressiveMediaSource
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector
import com.google.android.exoplayer2.ui.AspectRatioFrameLayout
import com.google.android.exoplayer2.ui.StyledPlayerView
import com.google.android.exoplayer2.ui.StyledPlayerView.SHOW_BUFFERING_NEVER
import com.google.android.exoplayer2.upstream.DataSource
import com.google.android.exoplayer2.upstream.DefaultHttpDataSource
import com.google.android.exoplayer2.upstream.FileDataSource
import com.google.android.exoplayer2.upstream.cache.*
import com.wn.common.constant.KEY_VIDEO_PLAY_LIST
import com.wn.common.constant.VIDEO_ACTIVITY_PLAYER
import com.wn.common.db.entity.VideoInfo
import com.wn.framework.base.BaseActivity
import com.wn.framework.utils.StatusBarSettingHelper
import com.wn.framework.utils.ViewUtils.setClipViewCornerRadius
import com.wn.video.adapter.VideoAdapter
import com.wn.video.databinding.ActivityVideoPlayBinding
import com.wn.video.listener.OnViewPagerListener
import com.wn.video.manager.PagerLayoutManager
import com.wn.video.view.RotateNoteView
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject

/**
 * @Author: wn
 * @Date: 2024/11/11 15:36
 * @Description: 视频播放页
 * 该页的结构 类似 抖音 短视频形式
 * 可以单独 使用其中 layout_video_item 页 做 视频播放页 传入 VideoInfo 即可
 * 页面整体 布局是 RecyclerView 的方式
 */
@Route(path = VIDEO_ACTIVITY_PLAYER)
@AndroidEntryPoint
class VideoPlayActivity : BaseActivity<ActivityVideoPlayBinding>() {

  override fun getLayoutRes() = R.layout.activity_video_play

  // 播放的位置
  private var mPlayingPosition = 0

  // 当前播放的Url
  private var mPlayUrl: String? = null

  // 自动播放
  private var mStartAutoPlay = true

  // 缓存对象
  private lateinit var mCache: Cache

  // ExoPlayer -- 播放器实例
  private var mExoPlayer: ExoPlayer? = null

  // 播放器插件 -- 容器
  private var mPlayView: StyledPlayerView? = null

  // 媒体资源加载工厂类
  private lateinit var mMediaSource: MediaSource.Factory

  // 动画 View -- 旋转音盒
  private var mRotateNoteView: RotateNoteView? = null

  // 视频列表 适配器
  @Inject
  lateinit var mAdapter: VideoAdapter

  /**
   * 直接获取 ARouter 传递的数据
   * 需要添加 @JvmField 注解 -- 使字段在Java中可见，从而正确实现注入
   */
  @Autowired(name = KEY_VIDEO_PLAY_LIST)
  @JvmField
  var mData: ArrayList<VideoInfo>? = null

  override fun initConfig() {
    ARouter.getInstance().inject(this)
  }

  override
  fun initView() {
    super.initView()
    StatusBarSettingHelper.setStatusBarTranslucent(this)
    StatusBarSettingHelper.statusBarLightMode(this, true)
    initPlayerView()
    initRecyclerView()
  }

  /**
   * 创建 exoplayer 播放器实例
   * 视屏画面渲染工厂类，语音选择器，缓存控制器
   * @return Boolean
   */
  private fun initPlayerView(): Boolean {
    /**
     * 创建 MediaSource 媒体资源 加载的工厂类
     * MediaSource 能够实现 边缓冲 边播放的 效果
     * 如需要播放 hls m3u8 则需要创建 DashMediaSource.Factory()
     */
    mMediaSource = ProgressiveMediaSource.Factory(buildCacheDataSource())
    // 创建 exoplayer 播放器容器
    mPlayView = initStylePlayView()
    // 创建 exoplayer 播放器实例
    mExoPlayer = initExoPlayer()
    mExoPlayer?.apply {
      // 缓冲完成 自动播放
      playWhenReady = mStartAutoPlay
      // 将播放器 容器 与 实例 绑定
      mPlayView?.player = this
      // 资源准备，如果设置 setPlayWhenReady(true) 则资源准备好就立马播放
      prepare()
    }
    /*
       val haveStartPosition = startItemIndex != C.INDEX_UNSET
       if (haveStartPosition) {
         // 初始化播放位置
         mExoPlayer?.seekTo(startItemIndex, startPosition)
       }
       // 设置 ExoPlayer 需要播放的多媒体item
       mExoPlayer?.setMediaItems(mediaItems  *//* resetPosition= !haveStartPosition*//*)

  */
    return true
  }

  private fun initRecyclerView() {
    val manager = PagerLayoutManager(this@VideoPlayActivity, LinearLayoutManager.VERTICAL, false)
    manager.setOnViewPagerListener(onViewPagerListener)
    mBinding.apply {
      recyclerView.layoutManager = manager
      recyclerView.adapter = mAdapter
      mAdapter.setData(mData)
      // list 传递 点击事件
      mAdapter.onItemClickListener = { _, _ ->
        mExoPlayer?.also {
          // 正在播放 暂停按钮出现 ,点击 暂停,反之。。。
          this.ivVideoPause.isVisible = it.isPlaying
          it.playWhenReady = !it.isPlaying
        }
      }
      // 设置View圆角
      setClipViewCornerRadius(tvRetry, ConvertUtils.dp2px(4.0F))

      ivBack.setOnClickListener {
        finish()
      }
      tvRetry.setOnClickListener {
        // 重试
        mExoPlayer?.prepare()
      }
    }
  }

  /**
   * 创建 exoplayer 播放器实例
   * @return StyledPlayerView
   */
  private fun initStylePlayView(): StyledPlayerView = StyledPlayerView(this).apply {
    controllerShowTimeoutMs = 10000
    setKeepContentOnPlayerReset(false)
    setShowBuffering(SHOW_BUFFERING_NEVER) // 不展示缓冲 View
    resizeMode = AspectRatioFrameLayout.RESIZE_MODE_FIT
    useController = false // 是否使用默认控制器,如需要可以参考 PlayerControlView
  }

  /**
   * CacheDataSourceFactory
   * 创建能够 边播放边缓存的 本地资源加载和http网络数据写入的工厂类
   * Cache cache：缓存写入策略和缓存写入位置的对象
   * DataSource.Factory upstreamFactory：http视频资源如何加载的工厂对象
   * DataSource.Factory cacheReadDataSourceFactory：本地缓存数据如何读取的工厂对象
   * DataSink.Factory cacheWriteDataSinkFactory：http网络数据如何写入本地缓存的工厂对象
   * CacheDataSource.Flags int flags：加载本地缓存数据进行播放时的策略,如果遇到该文件正在被写入数据,或读取缓存数据发生错误时的策略
   * CacheDataSource.EventListener eventListener  缓存数据读取的回调
   */
  private fun buildCacheDataSource(): DataSource.Factory {
    // 创建 http视频资源如何加载的工厂对象
    val upstreamFactory = DefaultHttpDataSource.Factory()
//        val cacheFile = getExternalFilesDir( /* type= */null) ?: filesDir
//        val downloadContentDirectory: File = File(cacheFile, "video")

    // 创建缓存，指定缓存位置，和缓存策略,为最近最少使用原则,最大为 200m
    mCache = SimpleCache(
      application.cacheDir,
      LeastRecentlyUsedCacheEvictor(1024 * 1024 * 200),
      StandaloneDatabaseProvider(this)
    )

    // 把缓存对象 cache 和负责缓存数据读取、写入的工厂类CacheDataSinkFactory 相关联
    val cacheDataSinkFactory =
      CacheDataSink.Factory().setCache(mCache).setFragmentSize(Long.MAX_VALUE)
    return CacheDataSource.Factory()
      .setCache(mCache)
      .setUpstreamDataSourceFactory(upstreamFactory)
      .setCacheReadDataSourceFactory(FileDataSource.Factory())
      .setCacheWriteDataSinkFactory(cacheDataSinkFactory)
      .setFlags(CacheDataSource.FLAG_IGNORE_CACHE_ON_ERROR)
  }

  /**
   * 初始化 ExoPlayer
   * @return ExoPlayer
   */
  private fun initExoPlayer(): ExoPlayer {
    // val playerBuilder = ExoPlayer.Builder(this)
    val playerBuilder = ExoPlayer.Builder(this).setMediaSourceFactory(mMediaSource)
    // 视频每一帧的画面如何渲染,实现默认的实现类
    val renderersFactory: RenderersFactory = DefaultRenderersFactory(this)
    playerBuilder.setRenderersFactory(renderersFactory)
    // 视频的音视频轨道如何加载,使用默认的轨道选择器
    playerBuilder.setTrackSelector(DefaultTrackSelector(this))
    // 视频缓存控制逻辑,使用默认的即可
    playerBuilder.setLoadControl(DefaultLoadControl())

    return playerBuilder.build()
  }

  /**
   * 滑动监听
   */
  private val onViewPagerListener = object : OnViewPagerListener {
    /**
     * 初始化完成
     */
    override fun onInitComplete(view: View?) {
      // 播放 第一个
      startPlay(0, view)
    }

    /**
     * 释放
     */
    override fun onPageRelease(isNext: Boolean, position: Int, view: View?) {
      // 停止动画
      view?.findViewById<RotateNoteView>(R.id.rotate_note_view)?.stopAnim()
      // 还应该暂停掉列表上正在播放的那个
    }

    /**
     * 选中
     */
    override fun onPageSelected(position: Int, isBottom: Boolean, view: View?) {
      // 越界 直接返回
      if (position < 0 || position >= mAdapter.itemCount) return
      // 点击了一个位置 直接返回
      if (position == mPlayingPosition) return
      mPlayingPosition = position
      // 播放 选中的
      startPlay(position, view)
    }
  }

  /**
   * 播放 哪一个
   * @param position Int -- 哪一个
   * @param view View?
   */
  private fun startPlay(position: Int, view: View?) {
    // 播放器视图
    if (view == null) return
    val item = mAdapter.getItem(position)
    if (item == null || item.playUrl?.isEmpty() == true) return
    showErrorView(false)
    // 如果父容器不等于this,则把 playView 添加进去
    // parent如果不为空则是被添加到别的容器中了，需要移除
    (mPlayView?.parent as? FrameLayout)?.removeAllViews()
    val frameLayout = view.findViewById<FrameLayout>(R.id.fl_container)
    mRotateNoteView = view.findViewById(R.id.rotate_note_view)
    mRotateNoteView?.initAnimator()
    mRotateNoteView?.startAnim()
    // 添加播放器
    frameLayout.addView(mPlayView)

    // 判断当前播放的和即将播放的是否同一个媒体资源
    // 如果是同一个视频资源,则不需要从重新创建mediaSource
    if (mPlayUrl != item.playUrl) {
      showLoading()
      val mediaSource: MediaSource =
        mMediaSource.createMediaSource(MediaItem.fromUri(item.playUrl ?: ""))
      // 设置ExoPlayer需要播放的多媒体item
      mExoPlayer?.setMediaSource(mediaSource)
      mExoPlayer?.prepare()

      // 构建媒体播放的一个Item， 一个item就是一个播放的多媒体文件
//        val mediaItem = MediaItem.fromUri("https://vdn1.vzuu.com/HD/c8af2fd6-438d-11eb-991f-da1190f1515e.mp4")
      // 设置ExoPlayer需要播放的多媒体item
//        mExoPlayer?.setMediaItem(mediaItem)

      mExoPlayer?.repeatMode = Player.REPEAT_MODE_ONE // 无限循环
      mPlayUrl = item.playUrl
      // 但需要onPlayerStateChanged 否则不会触发onPlayerStateChanged()
      mExoPlayer?.addListener(playerBackListener)
    }

    mExoPlayer?.playWhenReady = true// 资源缓冲好后立马播放
  }

  /**
   * 播放监听回调
   */
  private val playerBackListener = object : Player.Listener {
    /**
     * 播放状态改变
     */
    @Deprecated("Deprecated in Java")
    override fun onPlayerStateChanged(playWhenReady: Boolean, playbackState: Int) {
      // 判断当前视屏是否已经准备好
      val mIsPlaying = mExoPlayer?.bufferedPosition != 0L && playWhenReady
      when (playbackState) {
        Player.STATE_IDLE -> { // 播放错误
          mBinding.ivVideoPause.isVisible = false
          dismissLoading()
        }
        Player.STATE_BUFFERING -> { // 缓冲中
          // mBuffer.setVisibility(View.VISIBLE)
          showLoading()
        }
        Player.STATE_READY -> { // 准备完毕，开始播放
          dismissLoading()
          // 播放中
          mBinding.ivVideoPause.isVisible = !mIsPlaying
        }
        Player.STATE_ENDED -> { // 播放完成，无限循环模式则不会回调这里
          // 播放完成
        }
      }
    }

    /**
     * 是否正在播放改变
     */
    override fun onIsPlayingChanged(isPlaying: Boolean) {
      super.onIsPlayingChanged(isPlaying)
      if (isPlaying) {
        mRotateNoteView?.startAnim()
//                mBinding.ivVideoPause.gone()
      } else {
        mRotateNoteView?.pauseAnim()
//                mBinding.ivVideoPause.visible()
      }
    }

    /**
     * 播放错误
     */
    override fun onPlayerError(error: PlaybackException) {
      super.onPlayerError(error)
      showErrorView(true)
      ToastUtils.showLong(getString(com.wn.framework.R.string.video_play_error))
    }
  }

  /**
   * 是否展示错误信息
   * @param show Boolean
   */
  private fun showErrorView(show: Boolean) {
    mBinding.apply {
      ivVideoPause.isVisible = !show
      tvRetry.isVisible = show
      tvPlayError.isVisible = show
      if (show) dismissLoading()
    }
  }

  /**
   * 释放视频播放资源
   */
  private fun releasePlayer() {
    // 清理ExoPlayer资源
    mExoPlayer?.apply {
      playWhenReady = false
      stop()
      release()
      removeListener(playerBackListener)
    }
    mExoPlayer = null
    // 是否播放器资源
    mPlayView?.adViewGroup?.removeAllViews()
    mPlayView?.player = null
    mPlayView = null
    // 清除动画
    mRotateNoteView?.stopAnim()
    mRotateNoteView = null
    // 清理缓存
    mCache.release()
  }

  override fun onResume() {
    super.onResume()
    if (mExoPlayer?.isPlaying == false) {
      mExoPlayer?.playWhenReady = true
    }
  }

  override fun onPause() {
    super.onPause()
    if (mExoPlayer?.isPlaying == true) {
      mExoPlayer?.playWhenReady = false
    }
  }

  override fun onDestroy() {
    releasePlayer()
    super.onDestroy()
  }
}